package com.kongjs.application.service;

import com.kongjs.application.properties.GitProperties;
import com.kongjs.application.properties.GitRepositoryProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.git.GitModuleProperties;
import org.apache.sshd.git.transport.GitSshdSessionFactory;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.transport.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class GitOperationService {
    private final GitProperties gitProperties;
    private final Map<String, GitRepositoryProperties> repositoryMap = new ConcurrentHashMap<>();
    private static final CredentialsProvider noCredentialsProvider = new CredentialsProvider() {
        @Override
        public boolean isInteractive() {
            return false;
        }

        @Override
        public boolean supports(CredentialItem... credentialItems) {
            return true;
        }

        @Override
        public boolean get(URIish urIish, CredentialItem... credentialItems) throws UnsupportedCredentialItem {
            return true;
        }
    };

    public GitOperationService(GitProperties gitProperties) {
        this.gitProperties = gitProperties;
        this.init();
    }

    private void init() {
        List<GitRepositoryProperties> repositories = gitProperties.getRepositories();
        for (GitRepositoryProperties repository : repositories) {
            Path dir = Path.of(repository.getDir());
            if (Files.isDirectory(dir)) {
                try (Git git = Git.open(dir.toFile())) {
                    log.info("ok");
                } catch (IOException e) {
                    throw new RuntimeException(dir + "不是Git目录", e);
                }
            } else {
                if (Files.exists(dir)) {
                    throw new RuntimeException(dir + "不是Git目录");
                }
            }
            repositoryMap.put(repository.getId(), repository);
        }
    }


    public void pull(String id) {
        GitRepositoryProperties properties = repositoryMap.get(id);
        try (Git git = Git.open(new File(properties.getDir()))) {
            PullCommand pullCommand = git.pull()
                    .setProgressMonitor(new TextProgressMonitor());
            if (StringUtils.hasText(properties.getUsername()) && StringUtils.hasText(properties.getPassword())) {
                pullCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(properties.getUsername(), properties.getPassword()));
            } else {
                pullCommand.setCredentialsProvider(noCredentialsProvider);
            }
            PullResult pullResult = pullCommand.call();
            boolean successful = pullResult.isSuccessful();
            if (successful){
                boolean checkout = false;
                List<Ref> refs = git.branchList().call();
                for (Ref ref : refs) {
                    if (ref.getName().equals(properties.getBranch())) {
                        checkout = true;
                        break;
                    }
                }
                if (checkout){
                    CheckoutCommand checkoutCommand = git.checkout();
                    checkoutCommand.setName(properties.getBranch());
                    Ref ref = checkoutCommand.call();
                }
            }
        } catch (IOException | GitAPIException e) {
            throw new RuntimeException(e);
        }
    }

    public void clone(String id) {
        GitRepositoryProperties properties = repositoryMap.get(id);
        CloneCommand cloneCommand = Git.cloneRepository()
                .setURI(properties.getUri())
                .setDirectory(new File(properties.getDir()))
                .setProgressMonitor(new TextProgressMonitor());
        if (StringUtils.hasText(properties.getUsername()) && StringUtils.hasText(properties.getPassword())) {
            cloneCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(properties.getUsername(), properties.getPassword()));
        } else {
            cloneCommand.setCredentialsProvider(noCredentialsProvider);
        }
        try (Git git = cloneCommand.call()) {
            boolean checkout = false;
            List<Ref> refs = git.branchList().call();
            for (Ref ref : refs) {
                if (ref.getName().equals(properties.getBranch())) {
                    checkout = true;
                    break;
                }
            }
            if (checkout){
                CheckoutCommand checkoutCommand = git.checkout();
                checkoutCommand.setName(properties.getBranch());
                Ref ref = checkoutCommand.call();
            }
        } catch (GitAPIException e) {
            throw new RuntimeException(e);
        }
    }

    public void ssh(){
        SshClient client = SshClient.setUpDefaultClient();
        SshSessionFactory.setInstance(new GitSshdSessionFactory(client));
        PropertyResolver useProtocolV2 = PropertyResolverUtils
                .toPropertyResolver(
                        Collections.singletonMap(GitModuleProperties.GIT_PROTOCOL_VERSION.getName(),
                                GitProtocolConstants.VERSION_2_REQUEST));
        client.setParentPropertyResolver(useProtocolV2);

        client.stop();
        SshSessionFactory.setInstance(null);
    }
}
